如果你使用 Vue/React 脚手架搭建的项目, 你可能永远不需要这个步骤.
不过如果你需要手写 webpack 配置, 或者对 webpack 感兴趣, 也许你会疑惑: 添加了 hash 后的文件名每次打包都会变动, 如何将最新文件名写入到页面上?
答案是通过 html-webpack-plugin 插件将打包之后的文件名 Inject 到 index.html 中.
此逻辑仅仅需要配置几行代码
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html',
inject: true,
})
很简单, 所以我们一般不用花时间研究它.
不过我在一个项目中, index.html 不再由前端定义, 而是由服务端渲染输出, 所以 HtmlWebpackPlugin 这套逻辑不再走得通.
现在 后端就需要知晓打包后的文件名, 才能正确输出文件名到 index.html 中.
这个方案最显而易见与简单, 不过它却有一些缺点:
output中文件有很多, 包括Entry和异步Chunk, 由于我们无法区分Entry和其他文件, 只能手动指定要引入的Entry文件, 代码会像这样:
<script :src="files[entry-a.js]"></script>
<script :src="files[entry-b.js]"></script>
<script :src="files[entry-c.js]"></script>
并不优雅, 我想要一个循环搞定
如果我们要区分Entry和其他文件, 那么就只能从 Webpack 入手, 由于 Webpack 太强大(复杂), 我们需要在网上找找资料, 搜索关键字: get webpack hash
.
他们提到一个方案: 在插件中获取需要的文件名, 输入为一个清单文件.
有了这个清单文件, 后端就能读取它并注入到 index.html 中了.
不过它们提供的代码太简陋, 不能用于生产, 故继续查找资料来编写我所需要的插件:
同时在 html-webpack-plugin 插件有相同功能: 将打包好的文件 inject 到 index.html 中. 所以也去翻了翻它的源码.
最后搬运过来的代码就是这样:
class DumpAssetsPlugin {
/*
options: {
filename: 'dist/access.json', // default: outputPath + "/assets.json"
}
*/
constructor(options) {
options = options || {}
this.options = {
filename: options.filename || null,
};
}
apply(compiler) {
compiler.hooks.afterEmit.tap("ExportAssets", (compilation) => {
// see https://webpack.js.org/api/stats/
let stats = compilation.getStats().toJson();
let entrypoints = compilation.entrypoints;
let entryNames = Array.from(entrypoints.keys());
let files = []
for (let i = 0; i < entryNames.length; i++) {
const entryName = entryNames[i];
const entryFiles = entrypoints.get(entryName).getFiles();
files.push(...entryFiles)
}
function unique(arr) {
return arr.filter(function (item, index, arr) {
return arr.indexOf(item, 0) === index;
});
}
files = unique(files)
let assets = {
js: [],
css: [],
uncase: [] // 意料之外的文件
}
files.forEach(f => {
const sp = f.split('.')
const ext = sp[sp.length - 1]
if (assets[ext]) {
assets[ext].push(f)
} else {
console.warn('uncased file ext:', f)
assets.uncase.push(f)
}
})
let filename = this.options.filename
if (!filename) {
filename = stats.outputPath + "/assets.json"
}
require("fs").writeFileSync(
filename,
JSON.stringify(assets)
);
});
}
}
或者, 你也可以使用我上传到 NPM 的包: dump-assets-webpack-plugin
使用方法如下:
module.exports = {
entry: {
index: ['./src/index.js'],
},
output: {
path: __dirname + '/dist',
filename: 'js/[name].[chunkHash:8].js'
}
...
plugins: [
...
new DumpAssetsPlugin()
]
}